/* -*- c++ -*- */
#ifndef PMFONTS_H
#define PMFONTS_H

#include <stdlib.h>
#include <string.h>
#include <memory.h>
//#ifdef WIN32
//#include "wmaindef.h"
//#endif /* WIN32 */
#ifdef _WIN32
#ifndef WINGDIAPI
#define WINGDIAPI
#endif // WINGDIAPI
#ifndef APIENTRY
#if (_MSC_VER >= 800) || defined(_STDCALL_SUPPORTED) || defined(MINGW)
#define WINAPI      __stdcall
#define APIENTRY    WINAPI
#else
#define WINAPI
#define APIENTRY    WINAPI
#endif
#endif
#endif

#include <GL/gl.h>

typedef struct _pf_pixel {
  GLfloat _pix[ 4];
  _pf_pixel() {};
  _pf_pixel( const GLfloat data[ 4]) {
    _pix[ 0] = data[ 0];
    _pix[ 1] = data[ 1];
    _pix[ 2] = data[ 2];
    _pix[ 3] = data[ 3];
  };
  
  int operator==( const GLfloat data[ 4]) {
    int j;
    for ( j = 0; ( j < 4) && ( _pix[ j] == data[ j]); j++);
    return ( j == 4);
  };
} _T_pf_pixel;

class pf_letra {
 private:
  _pf_pixel *_pixels;
  GLint _width, _height, _size;
  GLubyte *_src;

 public:
  pf_letra(): _pixels( NULL), _width( 0), _height( 0), _size( 0), _src( NULL) {};
  pf_letra( const pf_letra &l): _pixels( NULL), _width( l._width),
                                _height( l._height), _size( l._size) {
    if ( _size) {
      int i;
      _pixels = new _pf_pixel[ _size];
      for ( i = 0; i < _size; i++)
        _pixels[ i] = l._pixels[ i];
      int src_size = _size >> 3; // _size / 8; es un bitmap
      _src = new GLubyte[ src_size];
      for ( i = 0; i < src_size; i++)
        _src[ i] = l._src[ i];
    }
  }

  ~pf_letra() {
    if ( _pixels)
      delete[] _pixels;
    if ( _src)
      delete[] _src;
  }

  GLint &width() { return _width;}
  GLint width() const { return _width;}
  GLint &height() { return _height;}
  GLint height() const { return _height;}
  GLint &size() { return _size;}
  GLint size() const { return _size;}
  
  GLint expand( const GLfloat foreground[ 4], const GLfloat background[ 4]) {
    if ( !_pixels)
      return 0;
    int i, j, src_size = _size >> 3; // _size / 8; es un bitmap
    // Ogl draws pixels down->up, and he have got it up->down
    if ( _width <= 8) {
      for ( j = 0, i = src_size - 1; i >= 0; i--) {
        int ibit = 0;
        for( GLubyte b = 0x80; b && ( ibit < _width); b = ( GLubyte)( b >> 1), j++, ibit++) {
          if ( _src[ i] & b) {
            _pixels[ j] = foreground;
          } else {
            _pixels[ j] = background;
          }
        }
      }
    } else { // _width > 8, && < = 16
      for ( j = 0, i = src_size - 2; i >= 0; i-=2) {
        int ibit = 0;
        for( GLubyte b = 0x80; b && ( ibit < _width); b = ( GLubyte)( b >> 1), j++, ibit++) {
          if ( _src[ i] & b) {
            _pixels[ j] = foreground;
          } else {
            _pixels[ j] = background;
          }
        }
        for( GLubyte b = 0x80; b && ( ibit < _width); b = ( GLubyte)( b >> 1), j++, ibit++) {
          if ( _src[ i + 1] & b) {
            _pixels[ j] = foreground;
          } else {
            _pixels[ j] = background;
          }
        }
      }
    }
    return i;
  }

  void set_base( const GLubyte *src, const GLint width_char, const GLint height_char, const GLint width_data) {
    if ( ( width_char == _width) && ( _height == height_char)) {
      int src_size = _size >> 3; // _size / 8; es un bitmap
      memcpy( _src, src, src_size);
    } else {
      _width = width_char;
      _height = height_char;
      _size = width_data * height_char;
      int src_size = _size >> 3; // _size / 8; es un bitmap
      if ( _src)
        delete[] _src;
      if ( _pixels)
        delete[] _pixels;
      if ( _size) {
        _src = new GLubyte[ src_size];
        _pixels = new _pf_pixel[ _size];
        for ( int i = 0; i < src_size; i++)
          _src[ i] = src[ i];
      } else {
        _src = NULL;
        _pixels = NULL;
      }
    }
  }

  void pintar() {
    if ( _pixels) {
      glDrawPixels( _width, _height, GL_RGBA, GL_FLOAT, _pixels);
      glBitmap( 0, 0, 0.0, 0.0, ( GLfloat)_width, 0.0, NULL);
    }
  }

};

typedef struct _pf_fuente {
  GLint width, height;
  GLint n;
  GLubyte *lit;
} _T_pf_fuente;

class pf_fuente {
 private:
  pf_letra *_letras;
  GLint _cuantas;
  GLuint _listbase;
  GLsizei _nlists;
  bool _has_transparency;

  void createGLLists() {
    if ( _listbase)
      glDeleteLists( _listbase, _nlists);
    _nlists = _cuantas;
    _listbase = glGenLists( _nlists);

    // por si acaso
    glPushAttrib( GL_ENABLE_BIT);
    glDisable(GL_DITHER);
    glDisable(GL_LIGHTING);
    int swapbytes;
    glGetIntegerv( GL_UNPACK_SWAP_BYTES, &swapbytes);
    glPixelStorei( GL_UNPACK_SWAP_BYTES, 0);
    for( int i = 0; i < _cuantas; i++) {
      glNewList( _listbase + i, GL_COMPILE);
      _letras[ i].pintar();
      glEndList();
    }
    glPixelStorei( GL_UNPACK_SWAP_BYTES, swapbytes);
    glPopAttrib();
  }

 public:
  pf_fuente(): _letras( NULL), _cuantas( 0), _listbase( 0), _nlists( 0), _has_transparency( false) {};
  pf_fuente( const pf_fuente &f): _letras( NULL), _cuantas( f._cuantas), _has_transparency( f._has_transparency) {
    if ( _cuantas) {
      _letras = new pf_letra[ _cuantas];
      for ( int i = 0; i < _cuantas; i++)
        _letras[ i] = f._letras[ i];
    }
    createGLLists();
  }

  ~pf_fuente() {
    if ( _letras)
      delete[] _letras;
    //if ( _listbase)
    //  glDeleteLists( _listbase, _nlists);
  }

  bool set_transparency( const GLfloat foreground[ 4], const GLfloat background[ 4]) {
    _has_transparency = ( foreground[ 3] != 1.0f) || ( background[ 3] != 1.0f);
    return _has_transparency;
  }
  
  int set( const _pf_fuente *fnt, const GLfloat fg[ 4], const GLfloat bg[ 4]) {
    if ( fnt->n) {
      _cuantas = fnt->n;
      if ( _letras)
        delete[] _letras;
      _letras = new pf_letra[ _cuantas];
      GLint width_data = 8;
      GLint num_bytes = 1;
      if ( fnt->width > 8) {
        num_bytes = 2;
        width_data = 16;
      }
      for( int i = 0; i < _cuantas; i++) {
        int idx_char = i * fnt->height * num_bytes;
        _letras[ i].set_base( &( fnt->lit[ idx_char]), fnt->width, fnt->height, width_data);
        _letras[ i].expand( fg, bg);
      }
      createGLLists();
      if ( _letras)
        delete[] _letras;
      _letras = NULL;
      set_transparency( fg, bg);
      return 1;
    } else {
      return 0;
    }
  }

  //GLuint &BaseList() { return _listbase;}
  GLuint BaseList() const { return _listbase;}


  GLsizei print( const char *s) {
    if ( !s || !*s) return 0;
    GLsizei tam_s = ( GLsizei)strlen(s);
    GLenum attrib = GL_LIST_BIT;
    if ( _has_transparency) {
      attrib |= GL_COLOR_BUFFER_BIT;
      glEnable( GL_BLEND);
      glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    }
    glPushAttrib( attrib);
    glListBase( _listbase);
    glCallLists( tam_s, GL_UNSIGNED_BYTE, (unsigned char *)s);
    glPopAttrib();
    return tam_s;
  }

};

//void lee_fuente( fuente &f, const GLfloat fg[ 4], const GLfloat bg[ 4]);
// fuentes disponibles:
//   { "fixed8x13dos", &_30}, { "fixed8x11", &_36}, { "fixed8x10", &_40}, { "fixed8x8", &_50},
//   { "decorate8x12", &_decorate}, { "mini8x8", &_mini},
//   { "fixed4x6", &_fixed_4x6_font}, // pmfont_4x6.h
//   { "fixed5x7", &_fixed_5x7_font}, // pmfont_5x7.h
//   { "fixed5x8", &_fixed_5x8_font}, // pmfont_5x8.h
//   { "fixed6x9", &_fixed_6x9_font}, // pmfont_6x9.h
//   { "fixed6x10", &_fixed_6x10_font}, // pmfont_6x10.h
//   { "fixed6x12", &_fixed_6x12_font}, // pmfont_6x12.h
//   { "fixed6x13", &_fixed_6x13_font}, // pmfont_6x13.h
//   { "fixed6x13bold", &_fixed_6x13B_font}, // pmfont_6x13B.h
//   { "fixed6x13italic", &_fixed_6x13O_font}, // pmfont_6x13O.h
//   { "fixed7x13", &_fixed_7x13_font}, // pmfont_7x13.h
//   { "fixed7x13bold", &_fixed_7x13B_font}, // pmfont_7x13B.h
//   { "fixed7x13italic", &_fixed_7x13O_font}, // pmfont_7x13O.h
//   { "fixed7x14", &_fixed_7x14_font}, // pmfont_7x14.h
//   { "fixed7x14bold", &_fixed_7x14B_font}, // pmfont_7x14B.h
//   { "fixed8x13", &_fixed_8x13_font}, // pmfont_8x13.h
//   { "fixed8x13bold", &_fixed_8x13B_font}, // pmfont_8x13B.h
//   { "fixed8x13italic", &_fixed_8x13O_font}, // pmfont_8x13O.h
//   { "clean6x12", &_fixed_clR6x12_font}, // pmfont_clR6x12.h
//   { "fixed9x15", &_fixed_9x15_font}, // pmfont_9x15.h
//   { "fixed9x15bold", &_fixed_9x15B_font}, // pmfont_9x15B.h
//   { "fixed9x18", &_fixed_9x18_font}, // pmfont_9x18.h
//   { "fixed9x18bold", &_fixed_9x18B_font}, // pmfont_9x18B.h
//   { "fixed10x20", &_fixed_10x20_font}, // pmfont_10x20.h

GLuint glGIDFont( const char *nueva_fuente, const GLfloat nuevo_fg[ 4], const GLfloat nuevo_bg[ 4]);
void glGIDDeleteFont( GLuint fnt_baselist);
int glGIDFontHeight( GLuint fnt_baselist);
int glGIDFontWidth( GLuint fnt_baselist);
const char *glGIDFontName( GLuint fnt_baselist);
GLsizei glGIDPrint( GLuint fnt_baselist, const char *s);
// returns a list of pmfont names ended with NULL and which should be freed
const char **glGIDGetAllPmFonts();

inline void copy_color( GLfloat dst[ 4], const GLfloat src[ 4]) {
  dst[ 0] = src[ 0];
  dst[ 1] = src[ 1];
  dst[ 2] = src[ 2];
  dst[ 3] = src[ 3];
}

class OGLFont {
public:
  OGLFont( const char *nueva_fuente, const GLfloat nuevo_fg[ 4], const GLfloat nuevo_bg[ 4]): _id_fnt( 0) {
    _id_fnt = glGIDFont( nueva_fuente, nuevo_fg, nuevo_bg);
    copy_color( _fg, nuevo_fg);
    copy_color( _bg, nuevo_bg);
  }
  OGLFont( const char *nueva_fuente): _id_fnt( 0) {
    GLfloat fondo[ 4] = { 0.0f, 0.0f, 0.0f, 0.0f};
    GLfloat letra[ 4] = { 1.0f, 1.0f, 1.0f, 1.0f};
    _id_fnt = glGIDFont( nueva_fuente, letra, fondo);
    copy_color( _fg, fondo);
    copy_color( _bg, letra);
  }
  OGLFont(): _id_fnt( 0) {
    GLfloat fondo[ 4] = { 0.0f, 0.0f, 0.0f, 0.0f};
    GLfloat letra[ 4] = { 1.0f, 1.0f, 1.0f, 1.0f};
    _id_fnt = glGIDFont( "fixed8x13", letra, fondo);
    copy_color( _fg, fondo);
    copy_color( _bg, letra);
  }
  OGLFont( const OGLFont &p): _id_fnt( p._id_fnt) {};
  GLsizei print( const char *str) {
    return glGIDPrint( _id_fnt, str);
  }

  bool font_ok() const { return _id_fnt != 0;}

  int width() const { return glGIDFontWidth( _id_fnt);}
  int height() const { return glGIDFontHeight( _id_fnt);}
  const char *name() const { return glGIDFontName( _id_fnt);}

  int set_font( const char *name) {
    GLuint new_font = glGIDFont( name, _fg, _bg);
    int ret = 0;
    if ( new_font) {
      _id_fnt = new_font;
      ret = 1;
    }
    return ret;
  }
  
private:
  GLuint _id_fnt;
  GLfloat _fg[ 4], _bg[ 4];
};

#endif
